\documentclass[11pt]{article}
%% \topmargin -1.5cm        % read Lamport p.163
%% \oddsidemargin -0.04cm   % read Lamport p.163
%% \evensidemargin -0.04cm  % same as oddsidemargin but for left-hand pages
%% \textwidth 16.59cm
%% \textheight 21.94cm 
%$ %\pagestyle{empty}       % Uncomment if don't want page numbers
\parskip 7.2pt           % sets spacing between paragraphs
%% %\renewcommand{\baselinestretch}{1.5} % Uncomment for 1.5 spacing between lines
\parindent 0pt		 % sets leading space for paragraphs

\usepackage{fullpage}
\usepackage{listings} % For source code
\usepackage[usenames,dvipsnames]{color} % For colors and names
\usepackage[pdftex]{graphicx}
\usepackage{subfigure}

\newcommand{\superscript}[1]{\ensuremath{^{\textrm{#1}}}}
\newcommand{\subscript}[1]{\ensuremath{_{\textrm{#1}}}}

\definecolor{mygrey}{gray}{.96} % Light Grey
\lstset{ 
        language=[ISO]C++,              
        tabsize=3,                                                     
        basicstyle=\tiny,               
        numbers=left,                   
        numberstyle=\tiny,              
        stepnumber=2,                   
        numbersep=5pt,                  
        backgroundcolor=\color{white}, 
        %showspaces=false,              
        %showstringspaces=false,        
        %showtabs=false,                
        frame=single,                    
        tabsize=3,                          
        captionpos=b,                   
        breaklines=true,                
        breakatwhitespace=false,        
        %escapeinside={\%*}{*)},        
        commentstyle=\color{BrickRed}   
}

\title{Northeastern University \\
  Department of Electrical and Computer Engineering \\
  - \\
  ECE5667 \\
  Lab 3 - Digital Delay/Reverb Using Software Circular Buffers}
\date{\today}
\author{
  Instructor: Deniz Erdogmus \\
  TA: Hooman Nezamfar \\
  Lab Partner: Andrew Lai \\
  - \\
  Author's Name: Paul Ozog
}

\begin{document}

\begin{titlepage}
  \maketitle
  \thispagestyle{empty}
\end{titlepage}

\tableofcontents
\pagebreak

\section{Introduction}
In this lab, we use our ADSP-BF535 EZ-Kit Lite to implement delay on an audio signal from a microphone using the three elementary DSP operations: shift, scale, and add.  We design a software circular buffer to indefinitely store samples from the serial port, and to send past samples to the serial port.  

Though the board supports hardware circular buffers, the exercises in this lab will provide us a working knowledge of circular buffers.  This knowledge will lead to implementing FIR filters that can simulate delay and reverberation using a finite amount of memory.

\section{Results and Analysis}
\subsection{Part I Prelab Questions}
Before discussing the results of our experiment, it is important to understand the factors that effect the length of our delay, given our hardware configuration.  If our program were to provide {\it t} seconds of delay using circular software buffers, it's obvious that we must store
\begin{equation}
  N_c \times f_s \frac{samples}{second} \times t\;second
\end{equation}
samples in memory, where {\it f\subscript{s}} is the sampling rate and {\it N\subscript{c}} is the number of channels.  If every sample is a 'short' (occupying 2 bytes in memory), we must have access to
\begin{equation}
  N_c \times 2\frac{bytes}{sample} \times f_s \frac{samples}{second} \times t\;second
\end{equation}
bytes of memory to realize {\it t} seconds of delay.

Therefore, the higher our A/D and D/A sample rates and delay time, the more memory we need for our program.  To acheive 1 second of delay for a mono signal sampled at 48kHz, we need
\begin{math}
  1 \times 2 \times 48000 \times 1 = 96000\;bytes
\end{math}
of system memory for the buffer.  Also, we must consider that the processor requires enough time to do all the calculations for the filter before the arrival of the next sample.

\subsection{Exercise 1}
For Exercise 1 and 2, we circular buffer called CircBuff for the left (mono) channel.  We declared the following variables, data structures, and constants in the initial implementation of our circular buffer.
\begin{itemize}
\item ARRAY\_SIZE: number of samples in CircBuff (constant)
\item short[ARRAY\_SIZE] CircBuff: array of samples for a single channel.
\item int ind: current index of the circular buffer.  Initialized to 0.  Should always be between 0 and ARRAY\_SIZE
\end{itemize}

CircBuff and ind are declared globally in ``Process Audio Data.c.'' ARRAY\_SIZE is a constant defined in ``BF535 Talkthrough.h.''  It is determined by multiplying the desired delay time (in seconds) by the sample rate (in samples per second).

The processing of samples should be done in the Process\_Audio\_Data() function, which is were our circular buffer was used.  This behavior to due the Talkthrough program's use of an interrupt to call Process\_Audio\_Data() when a new sample from the serial port is received.

The code for the body of Process\_Audio\_Data() becomes:
\begin{verbatim}
void Process_Audio_Data (void) {
  //Store the current sample from the serial input (mono)
  CircBuff[ind] = sLeft_Channel_In;
  
  //Pass the sample that was stored ARRAY_SIZE samples ago to the serial input
  sLeft_Channel_Out  = CircBuff[(ind+1) % ARRAY_SIZE];
  sRight_Channel_Out = CircBuff[(ind+1) % ARRAY_SIZE];
  
  //Increment ind, making sure it's between 0 and ARRAY_SIZE - 1
  ind = (ind < ARRAY_SIZE - 1) ? ind + 1 : 0;
  
  sAC97_Tag_Out = sAC97_Tag_In;
  sNew_Sample_Received = 0;
}
\end{verbatim}

The circular buffer for this exercise contains the exact number of samples in the delay period.  For instance, a delay of 500ms requires a sample array with ARRAY\_SIZE
\begin{math}
  = 48000 \times .5 = 24000
\end{math}
samples.  One should also note that because the microphone is a mono input, we need one circular buffer to store one channel's worth of samples for the required delay.  


In the file \texttt{BF535 Talkthrough.h}, we added the ARRAY\_SIZE constants in such a way so we can easily change the behavior by commenting/uncommenting the appropriate lines:
\begin{verbatim}
//#define ARRAY_SIZE 4800  // 100ms
//#define ARRAY_SIZE 24000 // 500ms 
//#define ARRAY_SIZE 48000 // 1s
#define ARRAY_SIZE 96000 // 2s
\end{verbatim}

\subsection{Exercise 2}
To measure on the results of our program, we provided the mic with an ``impulse'' (tapped the mic with our finger) and measured the arrival of the delayed impulse with a stopwatch.  The results of the program for delay times of 100ms, 500ms, 1s, and 2s are given in Table 1.

Note that these values should not be taken too strictly.  Clearly, our method of measurement has large potential for human error.  However, the results demonstrate that our design works correctly.
\begin{table}[h]
\caption{Exercise 2 Results}
\centering
\begin{tabular} {c c}
\hline\hline
Specified Delay time (sec) & Measured Delay (sec) \\ [0.5ex]
\hline
.100 & .93 \\
.500 & .503 \\ 
1.0  & .980 \\
2.0  & 2.05 \\ [1ex]
\hline
\end{tabular}
\label{table:ex1}
\end{table}

\subsection{Exercise 3}
Exercise 3 required the right speaker to produce a delayed signal, while the left should pass the signal through with no delay.  This required simply buffering the right channel, while keeping the left channel unbuffered.  The modifications to Process\_Audio\_Data were as follows:
\begin{verbatim}
...
//Delay the right channel, pass the left channel
sLeft_Channel_Out  = sLeft_Channel_In;
sRight_Channel_Out = CircBuff[(ind+1) % ARRAY_SIZE];
...
\end{verbatim}

We could qualitatively verify the behavior of the program by separating the left and right speakers and talking into the microphone.  By keeping our ears to the left speaker, we hear no delay from that speaker, but the right speaker clearly had delay.  

We observed correct operation when ARRAY\_SIZE was 24000 and 48000 for delay times of 500ms and 1000ms, respectively.  Since we didn't change the indexing scheme for the circular buffer, there was no need to measure the delay with a stopwatch like we did in Exercise 2.

\subsection{Part II Prelab Questions}
Because the filters in the remaining exercises must be able to index multiple samples from the circular buffer, we needed to generalize our circular indexing algorithm.  The FIR filters for the following exercises all had the form:
\begin{equation}
y[n] = a_0x[n] + a_1x[n - N] + a_2x[n - 2N] + a_3x[n - 3N] + a_4x[n - 4N]
\end{equation}
where N is given by
\begin{math}
\frac{t \times f_s}{4}
\end{math}
where {\it t} is the total reverb time desired.

One should note that in exercises 1-3, we always recall ARRAY\_SIZE samples away from the current index, which simplified way we indexed the circular buffer.  

In exercises 4-5, we use the following indexing algorithm (in C-like pseudo code):

\begin{verbatim}
i0 = ind; i1 = ind - N; i2 = ind - 2*N; i3 = ind - 3*N; i4 = ind - 4*N;

for each i in i0 ... i4:
 if i < 0:
  i += ARRAY_SIZE

yL = a0*LeftCircBuff[i0] + a1*LeftCircBuff[i1] + 
     a2*LeftCircBuff[i2] + a3*LeftCircBuff[i3] +
     a4*LeftCircBuff[i4]
yR = a0*RightCircBuff[i0] + a1*RightCircBuff[i1] + 
     a2*RightCircBuff[i2] + a3*RightCircBuff[i3] + 
     a4*RightCircBuff[i4]
\end{verbatim}
where LeftCircBuff, RightCircBuff, ind, and ARRAY\_SIZE are defined in ``BF535 Talkthrough.h'', along with N.  

\subsection{Exercise 4}
Because 1 second of total reverb is desired, the value of N for Exercise 4 is
\begin{equation}
  \frac{48000\;\frac{samples}{sec} \times 1\;second}{4} = 12000\;samples
\end{equation}

This value of N was placed in ``BF535 Talkthrough.h'' as a constant:
\begin{verbatim}
#define N 12000
\end{verbatim}

As instructed by the lab manual, the coefficients a\subscript{n} are defined as 
\begin{math}
  \left ( \frac{1}{2} \right )^n
\end{math}
where
\begin{math}
  0 \leq n \leq 4.
\end{math}
\begin{math}
  a_{0...4}
\end{math}
are defined globally in ``Process Audio Data.c'' as floats.  

Since the input is a mono microphone, there is only one circular buffer needed to provide reverb to the signal.  The implementation of Process\_Audio\_Data() for this exercise is shown below:

\begin{verbatim}
void Process_Audio_Data (void) {
  short LCH_val;
  int i1, i2, i3, i4, i5;
  i1 = ind; i2 = ind - N; i3 = ind - 2*N; i4 = ind - 3*N; i5 = ind - 4*N; 

  //check that all indexes are greater than 0
  i2 = (i2 < 0) ? i2 + ARRAY_SIZE : i2; i3 = (i3 < 0) ? i3 + ARRAY_SIZE : i3;
  i4 = (i4 < 0) ? i4 + ARRAY_SIZE : i4; i5 = (i5 < 0) ? i5 + ARRAY_SIZE : i5;
  
  //Buffer the left channel (mono)
  LeftCircBuff[ind] = sLeft_Channel_In;
  //Compute the outputs (y[n] in the lab manual)
  LCH_val = a*LeftCircBuff[i1] + b*LeftCircBuff[i2] +
            c*LeftCircBuff[i3] + d*LeftCircBuff[i4] + e*LeftCircBuff[i5];
  
  //Assign LCH_val to left/right outputs
  sLeft_Channel_Out = LCH_val;
  sRight_Channel_Out = LCH_val;
  
  sAC97_Tag_Out = sAC97_Tag_In;
  sNew_Sample_Received = 0;

  //Increment ind, making sure it's between 0 and ARRAY_SIZE - 1
  ind = (ind < ARRAY_SIZE - 1) ? ind + 1 : 0;
}
\end{verbatim}

\subsubsection{Quantitative Results}
To quantitatively measure the results of Exercise 4, we used a method similar to Exercise 2.  We provided an ``impulse'' by tapping the mic with our finger, and using a stopwatch to measure how long the reverberation lasted.  The quantitative results are summarized in Table 2.

\begin{table}[h]
  \caption{Exercise 4 Results}
  \centering
  \begin{tabular} {c c}
    \hline\hline
    Specified Reverb time (sec) & Measured Reverb Time (sec) \\ [0.5ex]
    \hline
    1.00 & 1.05 \\ [1ex]
    \hline
  \end{tabular}
  \label{table:ex4}
\end{table}

\pagebreak

Once again, these results are extremely prone to human error while stopping the stopwatch.  However, they confirm that our implementation of the circular buffer is working.  For further justification, we provide a MATLAB plot of the impulse response with respect to time in Figure \ref{fig:ex4}.  Clearly, we expect an impulse response to last exactly 1 second.

\begin{figure}[h]
  \begin{center}
    \includegraphics[width=80mm]{imp.png}
    \caption{Impulse response of reverb filter in Exercise 4}
    \label{fig:ex4}
  \end{center}
\end{figure}

\subsubsection{Qualitative Results}
The quality of the Exercise 4 reverb effect was not pleasant, and certainly not like a concert hall.  The effect sounded quite ``throbby,'' because the echoes were relatively loud and too far apart to produce a smooth reverb sound.  However, these results are consistent with logic: the given coefficients a\subscript{0...4} and overall reverb time of 1 second do not accurately simulate the physics of sound propagation in a large room (see Exercise 5).

\subsection{Exercise 5}
This exercise was very similar to exercise 4, however there were two differences.  First, the reverb was to more accurately model the attenuation and propagation speed of sound in a concert hall.  Second, the reverb was to be applied to a stereo signal.  To complete this exercise involved calculating more realistic values of N and 
\begin{math}
  a_{0...1}
\end{math}
coefficients.

\subsubsection{Calculation of N}
To create a pleasant reverb effect, we decided to simulate a difference in echo arrival time by 1 meter.  The value of N for this exercise is given by:
\begin{equation}
  N = \frac{df_s}{c} = \frac{1 \times 48000}{340} = 141
\end{equation}
where c is the propagation speed of sound waves in air.  Using 4 shifted coefficients, the total reverberation will last 
\begin{math}
  \frac{4 \times 141}{48000} = 0.12
\end{math}
seconds.  ARRAY\_SIZE had to be greater than 4*141, but small enough to not use too much memory.  We chose ARRAY\_SIZE to be 48000, giving us plenty of available samples in case we increased N.

\subsubsection{Calculation of Filter Coefficients}
The amplitude {\it A} of a sound wave in air after propagating {\it d} meters is given by the following equation:

\begin{equation}
  A = A_0 e^{\alpha d}
\end{equation}

where
\begin{math}
A_0
\end{math}
is the original amplitude, and 
\begin{math} 
  \alpha
\end{math}
is an attenuation constant.  

Choosing 
\begin{math}
  \alpha
\end{math}
 and 
\begin{math}
  A
\end{math}
to be 1, we use this expression to calculate 
\begin{math}
a_{0...4},
\end{math}
using a distance of 10m:

\begin{verbatim}
float a0 = 1;
float a1 = 2e-9;
float a2 = 4e-18;
float a3 = 8e-27;
float a4 = 1e-35;
\end{verbatim}

Because this exercise was to be used on a stereo input, we had to adjust the Process\_Audio\_Data function to buffer the right channel.  The modifications were as follows:

\begin{verbatim}
...
short RCH_val;
...
RightCircBuff[ind] = sRight_Channel_In;
RCH_val = a0*RightCircBuff[i1] + a1*RightCircBuff[i2] +
          a2*RightCircBuff[i3] + a3*RightCircBuff[i4] + e*RightCircBuff[i5];
sRight_Channel_Out = RCH_val;
...  
\end{verbatim}
note that because the overall buffer length is much shorter than Exercise 4, we may easily use two circular buffers without the possibility of running out of memory.

\subsubsection{Quantitative Results}
Those no need to measure the reverb time as in Exercise 4, because we have shown that the implementation of the circular buffer works correctly.  

\subsubsection{Qualitative Results}
The sound quality for this exercise's reverb was smoother and more realistic than the previous exercise.  The overall reverb lasted approximately a tenth of a second, and had the effect of sound waves propagating in a small amphitheater.  We attribute this observation to calculating FIR coefficients from physical equation describing sound wave propagation.

\section{Conclusion}
Using our implementation of a circular buffer, we have achieved audio reverb and delay effects.  For Part I, the percent error between measured total delay time and specified time is no more than 7.0\%.  This discrepancy is clearly due to human error of manually stopping a stopwatch.  Nevertheless, our implementation of a software circular buffer works correctly and can be applied to causal FIR filters, provided that we have enough memory and can compute every term before the arrival of the next sample.

In Part II, we used our circular buffer to implement digital reverb.  Without modeling the physics of sound, the reverb sounded unnatural, and certainly didn't sound like soundwaves in a concert hall.  Only after modeling the attenuation of sound using an exponential decay with an attenuation coefficient of 1.0 did we produce a realistic simulation of reverberation, with a total decay time of 0.12 seconds.  

\end{document}
